// Copyright 2006-2009 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef V8_PARSING_FUNC_NAME_INFERRER_H_
#define V8_PARSING_FUNC_NAME_INFERRER_H_

#include <vector>

#include "src/base/macros.h"
#include "src/pointer-with-payload.h"

namespace v8 {
namespace internal {

    class AstConsString;
    class AstRawString;
    class AstValueFactory;
    class FunctionLiteral;

    enum class InferName { kYes,
        kNo };

    template <>
    struct PointerWithPayloadTraits<AstRawString> {
        static constexpr int value = 2;
    };

    // FuncNameInferrer is a stateful class that is used to perform name
    // inference for anonymous functions during static analysis of source code.
    // Inference is performed in cases when an anonymous function is assigned
    // to a variable or a property (see test-func-name-inference.cc for examples.)
    //
    // The basic idea is that during parsing of LHSs of certain expressions
    // (assignments, declarations, object literals) we collect name strings,
    // and during parsing of the RHS, a function literal can be collected. After
    // parsing the RHS we can infer a name for function literals that do not have
    // a name.
    class FuncNameInferrer {
    public:
        explicit FuncNameInferrer(AstValueFactory* ast_value_factory);

        // To enter function name inference state, put a FuncNameInferrer::State
        // on the stack.
        class State {
        public:
            explicit State(FuncNameInferrer* fni)
                : fni_(fni)
                , top_(fni->names_stack_.size())
            {
                ++fni_->scope_depth_;
            }
            ~State()
            {
                DCHECK(fni_->IsOpen());
                fni_->names_stack_.resize(top_);
                --fni_->scope_depth_;
            }

        private:
            FuncNameInferrer* fni_;
            size_t top_;

            DISALLOW_COPY_AND_ASSIGN(State);
        };

        // Returns whether we have entered name collection state.
        bool IsOpen() const { return scope_depth_ > 0; }

        // Pushes an enclosing the name of enclosing function onto names stack.
        void PushEnclosingName(const AstRawString* name);

        // Pushes an encountered name onto names stack when in collection state.
        void PushLiteralName(const AstRawString* name);

        void PushVariableName(const AstRawString* name);

        // Adds a function to infer name for.
        void AddFunction(FunctionLiteral* func_to_infer)
        {
            if (IsOpen()) {
                funcs_to_infer_.push_back(func_to_infer);
            }
        }

        void RemoveLastFunction()
        {
            if (IsOpen() && !funcs_to_infer_.empty())
                funcs_to_infer_.pop_back();
        }

        void RemoveAsyncKeywordFromEnd();

        // Infers a function name and leaves names collection state.
        void Infer()
        {
            DCHECK(IsOpen());
            if (!funcs_to_infer_.empty())
                InferFunctionsNames();
        }

    private:
        enum NameType : uint8_t {
            kEnclosingConstructorName,
            kLiteralName,
            kVariableName
        };
        struct Name {
            // Needed for names_stack_.resize()
            Name() { UNREACHABLE(); }
            Name(const AstRawString* name, NameType type)
                : name_and_type_(name, type)
            {
            }

            PointerWithPayload<const AstRawString, NameType, 2> name_and_type_;
            inline const AstRawString* name() const
            {
                return name_and_type_.GetPointer();
            }
            inline NameType type() const { return name_and_type_.GetPayload(); }
        };

        // Constructs a full name in dotted notation from gathered names.
        const AstConsString* MakeNameFromStack();

        // Performs name inferring for added functions.
        void InferFunctionsNames();

        AstValueFactory* ast_value_factory_;
        std::vector<Name> names_stack_;
        std::vector<FunctionLiteral*> funcs_to_infer_;
        size_t scope_depth_ = 0;

        DISALLOW_COPY_AND_ASSIGN(FuncNameInferrer);
    };

} // namespace internal
} // namespace v8

#endif // V8_PARSING_FUNC_NAME_INFERRER_H_
